package jp.dodododo.aop.intercept;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import jp.dodododo.aop.annotation.Enhance;

import org.aopalliance.intercept.ConstructorInvocation;

@Enhance(false)
public class DelegateMethodInvocation extends AbstractMethodInvocation implements ConstructorInvocation {

	private Method delegateMethod;

	public DelegateMethodInvocation(Method delegateMethod, Object target, Class<?> targetClass, AccessibleObject staticPart,
			Object... arguments) {
		super(target, targetClass, staticPart, arguments);
		this.delegateMethod = delegateMethod;
	}

	public Object proceed() throws Throwable {
		try {
			return delegateMethod.invoke(getThis(), getArguments());
		} catch (InvocationTargetException e) {
			Throwable cause = e.getCause();
			if (cause != null) {
				throw cause;
			} else {
				throw e;
			}
		}
	}

	public Constructor<?> getConstructor() {
		AccessibleObject staticPart = getStaticPart();
		if (staticPart instanceof Constructor<?>) {
			return (Constructor<?>) staticPart;
		}
		return null;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (obj instanceof DelegateMethodInvocation == false) {
			return obj.equals(this);
		}
		DelegateMethodInvocation methodInvocation = (DelegateMethodInvocation) obj;
		if (this.getMethod().equals(methodInvocation.getMethod()) == false) {
			return false;
		}
		if (equals(this.getThis(), methodInvocation.getThis()) == false) {
			return false;
		}
		if (equals(this.getArguments(), methodInvocation.getArguments()) == false) {
			return false;
		}

		return true;
	}

	private boolean equals(Object[] arguments1, Object[] arguments2) {
		if (arguments1 == null && arguments2 == null) {
			return true;
		}
		if (arguments1 == null && arguments2 != null) {
			return false;
		}
		if (arguments1 != null && arguments2 == null) {
			return false;
		}
		if (arguments1.length != arguments2.length) {
			return false;
		}
		for (int i = 0; i < arguments1.length; i++) {
			Object arg1 = arguments1[i];
			Object arg2 = arguments2[i];
			if (equals(arg1, arg2) == false) {
				return false;
			}

		}
		return true;
	}

	private boolean equals(Object arg1, Object arg2) {
		if (arg1 == null && arg2 == null) {
			return true;
		}
		if (arg1 == arg2) {
			return true;
		}
		if (arg1 != null) {
			return arg1.equals(arg2);
		} else {
			return arg2.equals(arg1);
		}
	}

	@Override
	public int hashCode() {
		return getMethod().hashCode();
	}
}
